/** @file    ManImpl.cpp
 *  @author  Alessandro Polo
 *  @version $Id: ManImpl.cpp 2821 2010-08-01 11:44:26Z alex $
 *  @brief
 * File containing methods for the ManImpl class.
 * The header for this class can be found in ManImpl.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include "ManImpl.h"
 #include <core/Message.h>
 #include <core/Response.h>
 #include <core/MethodsCommon.h>
 #include <core/PersistenceManager.h>
 #include <core/SystemError.h>


//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// from ManProcessor.cpp
// int process_xml_xslt( const std::string& source_xml, const std::string& stylesheet, const std::string& output );
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

using namespace std;
using namespace wosh;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

ManImpl::ManImpl( BundleGeneric& bundle ) : BundleGenericWorker(bundle)
 {

 }

ManImpl::~ManImpl() {
	if ( isThreadRunning() ) {
		quitThread(10000);
	 }

 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////// RUN THREAD

void ManImpl::runThread()
 {
	Log(LOG_VERBOSE, ":runThread(): STARTING" );

	while( this->running )
	 {
		if ( this->msgQueue.isEmpty() ) {
			ThreadImpl::sleepForMSec(100);
			setThreadAlive();
			continue;
		 }
		ThreadImpl::sleepForMSec(50);
		setThreadAlive();

		// get and remove it from queue
		Message* message = this->msgQueue.pop();
		if ( message == NULL ) continue;

		evalMessage(message);

		// finally delete the instance
		delete message;
	 }

	Log(LOG_VERBOSE, ":runThread(): EXITING" );
 }

////////////////////////////////////////////////////////////////////////////////////////////////// RUN THREAD
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT ManImpl::evalMessage( Message* message )
 {
	if ( message == NULL || message->isEmpty() ) return WRET_ERR_PARAM;
	if ( !message->getContent()->isRequest() ) return WRET_ERR_PARAM;
	Request* request = dynamic_cast<Request*>(message->getContent());
	if ( request == NULL ) return WRET_ERR_PARAM;
	if ( !request->hasData() ) {
		return WRET_ERR_PARAM;
	 }

	WRESULT ret = WRET_OK;

	// eval target :
	//  - generic help of object/bundle ( 'woshX:Bundles/Discovery# man' )
	//  - item help of object/bundle ( 'woshX:Bundles/Discovery# man NotifyFrequency' )

	// RECOGNIZE PAGE !? HOW!?
	Variant dContext;
	Variant dTarget;
	request->extractArguments(dContext, dTarget);
	string target = dTarget.toString();
	string context = dContext.toString();
	// context is a URI ?
	getTargetByURI( target, URI(context) );

	// retrieve the object
	ManItem* mItem = loadManItem( context, target );
	if ( mItem == NULL ) {
		Log(LOG_CRITICAL, ":evalMessage(%"PRId64")", message->getID() );
		if ( request->doOmitResponse() )
			return WRET_ERR_INTERNAL;
		replyMessage( message, WRET_ERR_PARAM, new Variant(Utilities::format("Entry '%s' [%s] not found", context.c_str(), target.c_str())) );
		return WRET_ERR_INTERNAL;
	 }
	Log(LOG_VERBOSE, ":evalMessage(%"PRId64") : page: %s", message->getID(), mItem->getName().c_str() );

	// render
	int renderMode = 0;
	bool render_smart = true;
	if ( render_smart ) {
		if ( !message->getSource().getPath().empty() && message->getSource().getPath().at(0) == "Sessions" )
			renderMode = 1;
	 }
	ISerializable* content = renderManItem(mItem, renderMode);


	// send result / error
	replyMessage( message, ret, content );
	return ret;
 }

void ManImpl::replyMessage( const Message* message, WRESULT ret, ISerializable* content )
 {
	if ( message == NULL ) return;
	Response* response = new Response(_METHOD_Man, content, ret );
	Message* reply = message->replyTo();
	if ( false ) // dont emulate source
		reply->setSource(this->getOwner());
	reply->setContent(response);
	BusCore.postMessage(reply);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT ManImpl::getTargetByURI( string& target, const URI& uri) const
 {
	target = uri.getName();
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

ManItem* ManImpl::loadManItem( const string& name, const string& item )
 {
	ManItem* mItem = NULL;
//	string xmlfile = "$ROOT/var/man/" + name + ".man.xml";
	string xmlfile = "$ROOT/var/man";

	std::vector<ManItem*> objects_load;
	WRESULT ret = PersistenceManager::loadObjects( objects_load, "XML", xmlfile );
	if ( WFAILED(ret) ) {
		SystemError::raise(this->getOwner(), Log, SystemError::CRITICAL, 0, "loadManPage", "FAILED#%d loading Database %s..", ret, xmlfile.c_str() );
		return NULL;
	 }
	if ( objects_load.size() == 0 ) {
		Log(LOG_WARNING, ":loadManPage() : No Items found in database.." );
		return NULL;
	 }

	std::vector<ManItem*>::iterator it_load;
	for ( it_load=objects_load.begin(); it_load!=objects_load.end(); it_load++ ) {
		ManItem* currObj = *it_load;
		if ( currObj == NULL ) continue;
		if (  currObj->getName() != name ) {
			delete currObj;
			continue;
		 }
	//	currObj->getURI().setParent(this->getOwner()->getURI());
	//	currObj->getURI().setName(currObj->getName());
	//	currObj->setPermission( this->getOwner()->getPermission(), Permission::Overwrite_All, true );
		mItem = currObj;
	 }

	if ( mItem == NULL ) {
		return NULL;
	 }

	if ( item.empty() ) {
		return mItem;
	 }
	
	if ( !mItem->isKindOf<ManPage>() ) {
		return mItem;
	 }
	ManPage* page = dynamic_cast<ManPage*>(mItem);
	if ( page == NULL ) {
		return mItem;
	 }
	mItem= NULL;
	ManPage::tStrManSectionMap::Iterator it;
	const ManPage::tStrManSectionMap::ConstIterator it_end = page->getSections().end();
	for( it=page->getSections().begin(); it!=it_end; ++it ) {
		if ( it->second == NULL ) continue;
		ManItem* cItem = it->second->getItem().find(item);
		if ( cItem == NULL ) continue;
		mItem = cItem;
		it->second->getItem().set(item, NULL);
	 }
	delete page; page = NULL;
// deallocation BUG?
	return mItem;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

ISerializable* ManImpl::renderManItem( ManItem* mItem, int renderMode )
 {
	if ( mItem == NULL ) return NULL;

	switch(renderMode) {
		case 1: {
			Variant* data = new Variant( mItem->toString() );
			return data;
			}
		case 0:
		default: return mItem;
	 }
 }


//////////////////////////////////////////////////////////////////////////////////////////////////////////////
